想先說明以下觀念。
2.7.3 :081 > 1 + 1
=> 2
# + 在這裡是數字類別的方法
2.7.3 :082 > "1" + "1"
=> "11"
# + 在這裡是字串類別的方法
1 + "1"
或"1" + 1
會噴TypeError錯誤,是因為兩種類別的 + 方法,規定了所能運用的資料型態。而不是+方法自己規定了兩種資料型態不能相加。
自己動手做一個同名但不同類別都有的方法。
class A_class
def super_method
puts "我是A方法"
end
end
=> :super_method
class B_class
def super_method
puts "我是B方法"
end
end
=> :super_method
2.7.3 :096 > a = A_class.new
=> #<A_class:0x00007fcb083dbcf8>
2.7.3 :097 > a.super_method
我是A方法
=> nil
2.7.3 :098 > b = B_class.new
=> #<B_class:0x00007fcb083aa978>
2.7.3 :099 > b.super_method
我是B方法
=> nil
為何提這個是因為,類別裡常有"同名"的方法,在學習初期,我們常常會記Array也可以相加,但事實上是陣列也有自己的+
的方法。
2.7.3 :100 > [1, 2, 3] + [1, 2, 3]
=> [1, 2, 3, 1, 2, 3]
duck typing
請不要把下面文章想成原理,想成是一個故事。
Ruby是鴨子型別的設計風格。
「當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。」
我們把一開始的同名方法整理一下。
class Dog
def has_a_method
puts "我會叫,我會跑,我會游泳"
end
end
class Cat
def has_a_method
puts "我會叫,我會跑,我會游泳"
end
end
class Duck_look_like
def yes_i_can(another_animal)
another_animal.has_a_method
end
end
2.7.3 :252 > kind_of_animal = Duck_look_like.new
=> #<Duck_look_like:0x00007fa17b39dd10>
2.7.3 :253 > kitty = Dog.new
=> #<Dog:0x00007fa17b3265a8> #有一隻kitty
2.7.3 :254 > lucky = Cat.new
=> #<Cat:0x00007fa17b417c78> #有一隻lucky
#這邊故意反慣例,表示不在意他們本質。
2.7.3 :255 > kind_of_animal.yes_i_can(kitty)
我會叫,我會跑,我會游泳
=> nil
2.7.3 :256 > kind_of_animal.yes_i_can(lucky)
我會叫,我會跑,我會游泳
=> nil
我在做的不是規定鴨子是什麼,而是讓狗與貓變成像鴨子一樣。
初學時會說,那這一切都是設計好的呀?
無論有人說這種設計讓Ruby好學或是難學,好維護或難維護,但這種設計是為了我們能更快速操作"物件"。
關於duck typing
還會有其他衍生的問題,例如Monkey Patch
等,也是很好的討論問題,這邊稍微提到是因為明天會分享一些轉換類型的leetcode題型做分享。
能解決問題就是好魔法。
用"",與''包起來的任何字元都為字串。
2.7.3 :015 > "234234*(&%*(X34234.".class
=> String
2.7.3 :016 > '1+1=2'.class
=> String
2.7.3 :010 > "".class
=> String
2.7.3 :011 > ''.class
=> String
#即使""裡面是nil,也是字串。
我們來讓字串像其他類別吧。
2.7.3 :021 > "str" + "str"
=> "strstr"
2.7.3 :022 > "str"*8
=> "strstrstrstrstrstrstrstr"
2.7.3 :020 > str = String.new
=> ""
2.7.3 :023 > str << "123"
=> "123"
2.7.3 :024 > str << "456"
=> "123456"
2.7.3 :025 > "abc".bytes
=> [97, 98, 99]
#當然不只這些。
分享過的:leetcode.028:Implement strStr()
解法為利用index方法,由於已寫過就不重複拿來騙篇幅,直接整理如下。
def str_str(haystack, needle)
ans = haystack.index(needle)
ans != nil ? ans : -1
end
def str_str(haystack, needle)
haystack.index(needle) != nil ? haystack.index(needle) : -1
end
#看久了三元運算子很可愛,不常看覺得很討厭,跟不准家裡養寵物的爸媽,看到偷抱回來的毛小孩一樣。
陣列由於記憶體儲存方式,常會是用到迴圈,迭代,枚舉的好題型。
2.7.3 :026 > arr = Array.new
=> []
2.7.3 :027 > arr << 1
=> [1]
2.7.3 :028 > arr << 2
=> [1, 2]
2.7.3 :029 > arr << "3"
=> [1, 2, "3"]
# << 這個方法,陣列與字串的非常的不同。
#另外陣列也可以運用到運算符號,但使用結果與使用對象須注意
2.7.3 :031 > [1, 2, "3", 1, 2, "3"] - [1, 2, "3"]
=> []
2.7.3 :032 > [1, 1, 1] + [1, 2, 3]
=> [1, 1, 1, 1, 2, 3]
2.7.3 :033 > [1] * [1]
TypeError (no implicit conversion of Array into Integer)
2.7.3 :034 > [1] * 5
=> [1, 1, 1, 1, 1]
leetcode035:Search Insert Position
跟上題連結一樣。
#先不簡化
def search_insert(nums, target)
nums.each_with_index do |num, index|
if num >= target
return index
end #大於0的目標與裡面的值的關係是,剛好相等時回報位置,比較小時取代裡面值的位置
end
nums.size
end
def search_insert(nums, target)
nums.each_with_index do |num, index|
return index if num >= target
end
nums.size
end
Range用到new時,需要帶入正確參數,所以在解題上常會直接寫出自己要的範圍,不常會用到new。
2.7.3 :036 > Range.new
ArgumentError (wrong number of arguments (given 0, expected 2..3))
#也不是隨便帶參數就可以。
2.7.3 :045 > Range.new(1, 6)
=> 1..6
2.7.3 :046 > Range.new("a", 6)
ArgumentError (bad value for range)
2.7.3 :047 > Range.new("a", "b")
=> "a".."b"
#字串可以的原因,字母本身具有自己的字節數。
2.7.3 :007 > "a".bytes
=> [97]
2.7.3 :008 > "z".bytes
=> [122]
#很好玩的一點
2.7.3 :037 > 1..6.class
ArgumentError (bad value for range)
#字串,數字,陣列,雜湊我們自己輸入好就會new完成。範圍沒有。
2.7.3 :038 > (1..6).class
=> Range
一般解題剛接觸範圍,常見到會用在迴圈,例如:
for num in 1..array.size-1 do ... end
#或是..與...差異
2.7.3 :040 > (1..5).to_a
=> [1, 2, 3, 4, 5]
2.7.3 :041 > (1...5).to_a
=> [1, 2, 3, 4]
#另外不用to_a轉型寫法。
2.7.3 :043 > [*"a".."h"]
=> ["a", "b", "c", "d", "e", "f", "g", "h"]
leetcode.122:Best Time to Buy and Sell Stock II
#第一個是錯誤答案,第二個正確,主要是因為..與...差異。
def max_profit(prices)
max_profit = 0
for i in 0..(prices.size - 1)
max_profit += (prices[i+1] - prices[i]) if prices[i+1] > prices[i]
end
max_profit
end
def max_profit(prices)
max_profit = 0
for i in 0...(prices.size - 1)
max_profit += (prices[i+1] - prices[i]) if prices[i+1] > prices[i]
#prices[i+1] > prices[i] && max_profit += (prices[i+1] - prices[i])
# 上一句可改寫成這樣,久了會習慣,不習慣就是原本的比較好。
end
max_profit
end
習慣多利用each,map
def max_profit(prices)
max_profit = 0
prices.each_cons(2) do |price , next_price|
next_price > price && max_profit += next_price - price
end
max_profit
end
後面日程會再提到each,map。
若真有比我新的新手路過,先記得初期題型Range比較常被拿來利用,尤其在陣列身上。
又愛又恨...原因是ㄓ跟ㄗ,ㄔ跟ㄘ
先瞭解長相
2.7.3 :050 > {a: 123, :b => 123}.class
=> Hash
#前面是比較新的寫法,後面是箭頭式,都對。
2.7.3 :051 >{a: 123, :a => 123}.class
(irb):51: warning: key :a is duplicated and overwritten on line 51
=> Hash
#被覆蓋指向了,所以請確定Key沒用過。
2.7.3 :052 > {a: 123, "a": 123}.class
(irb):52: warning: key :a is duplicated and overwritten on line 52
=> Hash
#一樣被overwritten
2.7.3 :054 > {:a => 123, "a" => 123}.class
=> Hash
#又沒問題了....
會這樣的原因在昨日的符號。
新手初期手動寫雜湊請記得格式統一。
利用運算變成Hash的資料也是幫你統一格式,沒理由自己寫用兩種以上格式。
Hash在學習上除了知道找Key跟Value怎麼處理外,會有很多題型是將資料轉成Hash來處理。
2.7.3 :057 > hash = { a: 1, b: 2, c: 3}
=> {:a=>1, :b=>2, :c=>3}
2.7.3 :058 > hash.keys
=> [:a, :b, :c]
2.7.3 :059 > hash.values
=> [1, 2, 3]
#keys與values這兩個用復數,這慣例在Rails上也很常見,可以當成一種習慣。
2.7.3 :060 > hash[:a]
=> 1
2.7.3 :061 > hash[:b]
=> 2
2.7.3 :063 > hash.index(2)
=> :b
leetcode001:two_sum永遠勸退的第一題
def two_sum(nums, target)
new_hash = {}
nums.each_with_index do |num, index|
return [new_hash[num], index] if new_hash.has_key?(num)
new_hash[target - num] = index
end
end
def two_sum(numbers, target)
hash = {}
numbers.each_with_index do |num, i|
return [hash[target - num] +1 , i + 1] if hash[target - num]
hash[num] = i
end
end
#說明,文章連結都有喔。
利用枚舉產生的Hash,其中的Key與Value常是會有關聯性的,依照這個關聯性,當我們知道其中一個值後,能很快速知道另外一個的結果。
例如
2.7.3 :075 > [1, 2, 3, 3, 5, 6, 1, 2, 7].group_by {|num|num}
=> {1=>[1, 1], 2=>[2, 2], 3=>[3, 3], 5=>[5], 6=>[6], 7=>[7]}
2.7.3 :076 > [1, 2, 3, 3, 5, 6, 1, 2, 7].tally
=> {1=>2, 2=>2, 3=>2, 5=>1, 6=>1, 7=>1}
查表法難在建立表格,建好之後的快樂會讓你忘記建立時的痛苦。
Hash往往是被介紹的很少,但後面越用用多的資料類型,尤其到Rails後,可以發現所有實體都長的跟Hash沒什麼差別,刷題與學習Rails不一定有正比關係,但是練習越多,越不會害怕Hash,越能體會迭代與枚舉的好處。
今日提到的。
1.Duck Typing
2.一些曾經解過的leetcode題目。